package gogoqiu.java.base.http.utils;

import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;

import gogoqiu.java.base.console;
import gogoqiu.java.base.http.response.StandardResponse;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.GzipSource;

public abstract class BaseRespBodyProcess <ResponseType> {
    private final static String TAG = BaseStdRespListProcess.class.getSimpleName();

    public BaseRespBodyProcess(){                
    }

    public void parse( final String url, final Response response ) throws IOException{
        ResponseBody responseBody = response.body();

        long contentLength = responseBody.contentLength();
        //boolean successful = response.code()==200;
        boolean gzip = "gzip".equals(response.headers().get("Content-Encoding"));
        //boolean gsonParsedResult = false;
        //ParseStep step = ParseStep.respBody;
        //content = "";

        /*
            http ResponseBody可能为null吗
         * 状态码为 204 No Content 的响应，明确表示成功处理请求但无返回内容，此时 body 必然为空。
            某些 HEAD 方法的响应，仅返回头部信息，不包含 body。
         */
        if ( responseBody==null ) {
            this.OnHttpRespBodyEmpty( response.code() );
            return;
        }else{
            //step = ParseStep.getContent;
            String content = text( responseBody, gzip );
            if( content == null ){
                this.OnGetContentError( response.code() );
                return;
            }else{                
                //step = ParseStep.gson;
                //List<ResponseType> resps = json(content);
                ResponseType obj = json( content );
                if( obj ==  null ){
                    this.OnGsonError(url, response.code(), contentLength, content );
                }else{
                    // 22:34:52.723 [main] INFO  consoleOnly - obj=[{data=没有上传文件, retVal=false, code=0.0, msg=没有上传文件}], clz=ArrayList
                    // console.outputInfo( "obj={}, clz={}", obj, obj.getClass().getSimpleName() );                    
                    this.OnResps(obj);                    
                }
            }
        }
    }

    private String text(@NotNull ResponseBody responseBody, boolean gzip ) throws IOException{
        String body = null;
        
        BufferedSource source = responseBody.source();
        source.request(Long.MAX_VALUE);
        Buffer buffer = source.buffer();

        if ( gzip ) {
            GzipSource gzippedResponseBody = new GzipSource(buffer.clone());
            buffer = new Buffer();
            buffer.writeAll(gzippedResponseBody);
        }

        MediaType contentType = responseBody.contentType();
        Charset charset;
        if (contentType == null || contentType.charset(StandardCharsets.UTF_8) == null) {
            charset = StandardCharsets.UTF_8;
        } else {
            charset = contentType.charset(StandardCharsets.UTF_8);
        }
        assert charset != null;
        body = buffer.clone().readString ( charset );
        
        return body;
    }

    public abstract void OnHttpRespBodyEmpty( int code );
    public abstract void OnGetContentError( int code );
    public abstract void OnGsonError( String url, int code, long contentLength, String content );
    public abstract void OnResps( ResponseType obj );
    // 需要额外指明，而不是仅仅用<ResponseType>
    abstract public Type getGsonParseType();

    /*
     * Gson 在解析 JSON 时，如果没有明确指定目标类（比如用 Object.class 解析），
     * 会默认将 JSON 对象解析为 LinkedTreeMap（而非你的 StandardResponse 实例）。
     * 此时直接强转 LinkedTreeMap 到 StandardResponse 必然失败，因为它们是完全不同的类。
     */
    private ResponseType json( String body ){        
        if( body.isEmpty() )
            return null;
        Gson gson = new Gson();
        try{
            /*
            如果是List封装的数据，只能保证List的检测。无法投射到内部数据的转换
             */
            /*
            console.outputInfo( "type={}, anType={}", 
                new TypeToken<ResponseType>() { }.getType(),
                new TypeToken<List<StandardResponse>>(){}.getType());*/
            //ResponseType obj = gson.fromJson( body,  
            //    new TypeToken<ResponseType>() { }.getType() );         
            ResponseType obj = gson.fromJson( body, getGsonParseType() );       
            return obj;
        }catch ( Exception e ){
            return null;
        }
    }
}
